package com.fingard.dsp.bank.directbank.chinapay05;

import chinapay.Base64;
import chinapay.SecureLink;
import com.chinapay.secss.SecssUtil;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.FailCode;
import com.fingard.dsp.bank.directbank.SendBankRespose;
import com.fingard.dsp.bank.directbank.chinapay01.util.*;
import com.fingard.dsp.bank.directbank.chinapay05.util.HttpUtils;
import com.fingard.io.FileHelper;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

public class ChinaPay05Base extends DirectBase {
    protected String getCharset() {
        return getBankConfig().getCharset("UTF-8");
    }

    protected String getCharset(String p_def, String p_for) {
        return getBankConfig().getCharset(p_def, p_for);
    }

    public static String genHtmlResult(Map<String, String> data) {
        TreeMap<String, String> tree = new TreeMap<String, String>();
        Iterator<Entry<String, String>> it = data.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, String> en = it.next();
            tree.put(en.getKey(), en.getValue());
        }
        it = tree.entrySet().iterator();
        StringBuffer sf = new StringBuffer();
        while (it.hasNext()) {
            Entry<String, String> en = it.next();
            String key = en.getKey();
            String value = en.getValue();
            if (sf.length() > 0) {
                sf.append("&");
            }
            sf.append(key + "=" + value);
        }

        return sf.toString();
    }

    protected Map<String, String> signByRSA(Map<String, String> reqData, ActSetItem actItem) throws Exception {
        reqData = SDKUtil.filterBlank(reqData);
        reqData.put("certId", getSignCertId(actItem));

        String signData = SDKUtil.coverMap2String(reqData);
        WriteBankLogLn2("签名原始数据:\r\n" + signData);

        String signature = sign(signData, actItem);
        reqData.put("signature", signature);

        return reqData;
    }

    public String getSignCertId(ActSetItem actItem) {
        try {
            KeyStore keyStore = CertUtil.getKeyInfo(actItem.ownKeyStorePath, actItem.ownKeyPassword, "PKCS12");
            Enumeration<String> aliasenum = keyStore.aliases();
            String keyAlias = null;
            if (aliasenum.hasMoreElements()) {
                keyAlias = aliasenum.nextElement();
            }
            X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias);
            return cert.getSerialNumber().toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * SHA256WithRSA签名
     *
     * @param actItem
     * @param p_reqStr
     * @return
     * @throws Exception
     */
    public String sign(String p_reqStr, ActSetItem actItem) throws Exception {
        KeyStore ks = KeyStore.getInstance("PKCS12");
        FileInputStream fis = new FileInputStream(actItem.ownKeyStorePath);

        char[] keyPassword = null;
        if (StringHelper.hasAnyChar(actItem.ownKeyPassword)) {
            keyPassword = actItem.ownKeyPassword.toCharArray();
        }
        ks.load(fis, keyPassword);
        fis.close();
        fis = null;

        @SuppressWarnings("rawtypes")
        Enumeration enumAlias = ks.aliases();

        String keyAlias = null;
        if (enumAlias.hasMoreElements()) {
            keyAlias = (String) enumAlias.nextElement();
        }

        PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keyPassword);
        if (privateKey == null) {
            return null;
        }

        byte[] signDigest = SecureUtil.sha256X16(p_reqStr, getCharset());
        byte[] byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(privateKey, signDigest));

        return new String(byteSign);
    }

    public String sign(String msg, String merId, String actItem) throws Exception {
        String msg1 = new String(Base64.encode(msg.getBytes(getCharset())));
        WriteBankLogLn2("base64=====:" + msg1);
        chinapay.PrivateKey key = new chinapay.PrivateKey();
        key.buildKey(merId, 0, actItem);
        SecureLink s = new SecureLink(key);
        return s.Sign(msg1);
    }

    public String sign(String msg, String merId, String actItem,String p_for) throws Exception {
        String msg1 = new String(Base64.encode(msg.getBytes(getCharset("GBK",p_for))));
        WriteBankLogLn2("base64=====:" + msg1);
        chinapay.PrivateKey key = new chinapay.PrivateKey();
        key.buildKey(merId, 0, actItem);
        SecureLink s = new SecureLink(key);
        return s.Sign(msg1);
    }

    /**
     * SHA256WithRSA验签
     *
     * @param actItem
     * @param
     * @param
     * @return
     * @throws Exception
     */
    public boolean verifySignByRSA(Map<String, String> p_reqData, ActSetItem actItem) throws Exception {
        // 从HTTP响应数据获取公钥
        String strCert = p_reqData.get("signPubKeyCert");
        X509Certificate x509Cert = CertUtil.genCertificateByStr(strCert);
        if (x509Cert == null) {
            return false;
        }

        PublicKey publicKey = x509Cert.getPublicKey();
        if (publicKey == null) {
            return false;
        }

        // 3.验签
        String stringSign = p_reqData.get("signature");
        String verifyData = SDKUtil.coverMap2String(p_reqData);

        byte[] signBytes = SecureUtil.base64Decode(stringSign.getBytes(getCharset()));
        byte[] verifyBytes = SecureUtil.sha256X16(verifyData, getCharset());

        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(verifyBytes);

        return signature.verify(signBytes);
    }

    /**
     * byte[] 转 16进制字符串
     *
     * @param src
     * @return
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }

        return stringBuilder.toString();
    }

    /**
     * 发送数据到银行
     *
     * @param actItem
     * @param p_reqUrl
     * @return
     * @throws Exception
     */
    public Map<String, String> sendToBank(Map<String, String> p_reqMap, ActSetItem actItem, String p_reqUrl) throws Exception {
        Map<String, String> tmpMapReq = signByRSA(p_reqMap, actItem);

        WriteBankLogStep2(genHtmlResult(tmpMapReq));
        WriteBankUrlLogLn(p_reqUrl);

        Map<String, String> tmpMapRet = AcpService.post(tmpMapReq, p_reqUrl, getCharset());
        WriteBankLogStep3(genHtmlResult(tmpMapRet));

        return tmpMapRet;
    }

    public Map<String, String> sendDataToBank(Map<String, String> tmpMapReq, String p_reqUrl) throws Exception {
        Map<String, String> sendMap = new TreeMap<String, String>();
        Iterator<String> entries = tmpMapReq.keySet().iterator();
        while (entries.hasNext()) {
            String key = entries.next();
            // 排除特殊字段
            if (key.startsWith("__")) {
                continue;
            }
            // 排除空字段
            String value = tmpMapReq.get(key);
            if (StringHelper.isNullOrEmpty(value)) {
                continue;
            }
            sendMap.put(key, value);
        }

        WriteBankLogLn2("服务地址:" + p_reqUrl);
        WriteBankLogStep2(genHtmlResult(sendMap));

        String resp = HttpUtils.send(p_reqUrl, sendMap);
        String[] strs = resp.split("&", -1);
        Map<String, String> tmpMapRet = new TreeMap<String, String>();
        for (String str : strs) {
            String[] keyValues = str.split("=", -1);
            if (keyValues.length < 2) {
                continue;
            }
            String key = keyValues[0];
            String value = keyValues[1];
            if (StringHelper.isNullOrEmpty(value)) {
                continue;
            }
            value = URLDecoder.decode(value, getCharset());
            tmpMapRet.put(key, value);
        }
        WriteBankLogStep3(genHtmlResult(tmpMapRet));
        return tmpMapRet;
    }


    /**
     * 获取公钥
     *
     * @throws Exception
     */
    public PublicKey getPubCerFile(String pubCerPath) throws Exception {
        X509Certificate cert = null;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(pubCerPath);
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            cert = (X509Certificate) cf.generateCertificate(fis);
            fis.close();

            PublicKey publicKey = cert.getPublicKey();
            if (publicKey == null) {
                return null;
            }
            return publicKey;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    public PublicKey getEncryptCertPublicKey(ActSetItem actItem) {
        X509Certificate encryptCert = null;

        String path = actItem.ownCertFilePath;
        if (StringHelper.hasAnyChar(path)) {
            encryptCert = initCert(path);
            return encryptCert.getPublicKey();
        } else {
            return null;
        }
    }

    public String getEncryptCertId(ActSetItem actItem) {
        X509Certificate encryptCert = null;

        String path = actItem.ownCertFilePath;
        if (FileHelper.existFile(path)) {
            encryptCert = initCert(path);
            return encryptCert.getSerialNumber().toString();
        } else {
            LogUtil.writeErrorLog("acpsdk.encryptCert.path is empty");
            return null;
        }
    }

    static {
        try {
            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public X509Certificate initCert(String path) {
        X509Certificate encryptCertTemp = null;
        CertificateFactory cf = null;
        FileInputStream in = null;
        try {
            cf = CertificateFactory.getInstance("X.509", "BC");
            in = new FileInputStream(path);
            encryptCertTemp = (X509Certificate) cf.generateCertificate(in);
        } catch (CertificateException e) {
            e.printStackTrace();
            WriteBankLogLn(e);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            WriteBankLogLn(e);
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
            WriteBankLogLn(e);
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    WriteBankLogLn(e);
                }
            }
        }
        return encryptCertTemp;
    }

    public String encryptData(String data, String encoding, ActSetItem actItem) {
        return SecureUtil.encryptData(data, encoding, getEncryptCertPublicKey(actItem));
    }

    /**
     * 返回HTTP字符串转MAP
     *
     * @return
     */
    public static Map<String, String> convertToMap(String tmpStr) {
        Map<String, String> params = new HashMap<String, String>();

        String[] nodes = tmpStr.split("&");
        for (String node : nodes) {
            int index = node.indexOf("=");
            String key = node.substring(0, index);
            String value = node.substring(index + 1);
            params.put(key, value);
        }

        return params;
    }

    public String deCodeFileContent(Map<String, String> resData) {
        String filePath = null;
        String fileContent = resData.get("fileContent");
        if (StringHelper.hasAnyChar(fileContent)) {
            FileOutputStream out = null;
            try {
                byte[] fileArray = SDKUtil.inflater(SecureUtil.base64Decode(fileContent.getBytes(getCharset())));
                String fileDirectory = createTempFileDirectory();
                if (SDKUtil.isEmpty(resData.get("fileName"))) {
                    filePath = fileDirectory + File.separator + resData.get("merId") + "_" + resData.get("batchNo") + "_" + resData.get("txnTime") + ".txt";
                } else {
                    filePath = fileDirectory + File.separator + resData.get("fileName");
                }
                File file = new File(filePath);
                if (file.exists()) {
                    file.delete();
                }
                file.createNewFile();
                out = new FileOutputStream(file);
                out.write(fileArray, 0, fileArray.length);
                out.flush();
            } catch (UnsupportedEncodingException e) {
                LogUtil.writeErrorLog(e.getMessage(), e);
            } catch (IOException e) {
                LogUtil.writeErrorLog(e.getMessage(), e);
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return filePath;
    }

    /**
     * 获取线程等待时间列表
     *
     * @param tmpSleepTimeList
     * @return
     */
    public String[] getRealSleepTimeList(String tmpSleepTimeList) {
        String[] sleepTimeList = tmpSleepTimeList.split(",");

        StringBuilder realSleepTimeList = new StringBuilder();
        for (int i = 0; i < sleepTimeList.length; i++) {
            if (realSleepTimeList.length() > 0) {
                realSleepTimeList.append(",");
            }
            if (i == 0) {
                realSleepTimeList.append(sleepTimeList[0]);
            } else {
                int sleep = Integer.parseInt(sleepTimeList[i - 1]);
                int lastSleep = Integer.parseInt(sleepTimeList[i]);
                int realSleepTime = lastSleep - sleep;
                realSleepTimeList.append(realSleepTime);
            }
        }
        return realSleepTimeList.toString().split(",");
    }

    SecssUtil getSecssUtil(ActSetItem actItem) {
        SecssUtil secssUtil = new SecssUtil();
        Properties properties = new Properties();
        properties.setProperty("verify.file", actItem.oppCertFilePath);
        properties.setProperty("sign.filePath", actItem.ownKeyStorePath.substring(0, actItem.ownKeyStorePath.lastIndexOf(File.separator)));
        properties.setProperty("sign.file", actItem.ownKeyStorePath);
        properties.setProperty("sign.file.password", actItem.ownKeyPassword);
        properties.setProperty("sign.cert.type", "PKCS12");
        properties.setProperty("sign.invalid.fields", "Signature,CertId");
        properties.setProperty("signature.field", "Signature");
        secssUtil.init(properties);
        return secssUtil;

    }

    /**
     * 下载对账单到本地并读取压缩包中业务明细文件内容
     */
    protected SendBankRespose smsGet(String p_url, String filePath) {
        SendBankRespose retResp = new SendBankRespose();

        URL url = null;
        HttpURLConnection httpUrlConnection = null;
        InputStream fis = null;
        FileOutputStream fos = null;

        try {
            url = new URL(p_url);
            httpUrlConnection = (HttpURLConnection) url.openConnection();
            httpUrlConnection.setConnectTimeout(50 * 1000);
            httpUrlConnection.setDoInput(true);
            httpUrlConnection.setDoOutput(true);
            httpUrlConnection.setUseCaches(false);
            httpUrlConnection.setRequestMethod("GET");
            httpUrlConnection.setRequestProperty("Charset", "UTF-8");
            httpUrlConnection.connect();
            fis = httpUrlConnection.getInputStream();
            byte[] temp = new byte[1024];
            int b;
            fos = new FileOutputStream(new File(filePath));
            while ((b = fis.read(temp)) != -1) {
                fos.write(temp, 0, b);
                fos.flush();
            }
        } catch (IOException e) {
            retResp.connState = FailCode.E9999;
            retResp.retMessage = e.getMessage();
            retResp.appendLog(e);
            return retResp;
        } finally {
            try {
                if (fis != null) fis.close();
                if (fos != null) fos.close();
                if (httpUrlConnection != null) httpUrlConnection.disconnect();
            } catch (IOException e) {
                this.WriteBankLogLn(e);
            }
        }

        return retResp;
    }

    /**
     * 将元转分
     *
     * @param p_yuan
     * @return
     */
    public String amountMultiply100(String p_yuan) {
        if (StringHelper.isNullOrEmpty(p_yuan)) {
            return null;
        }
        return StringHelper.amountMultiply100(p_yuan);
    }

    /**
     * 将分转元
     *
     * @param p_cent
     * @return
     */
    public String amountDivide100(String p_cent) {
        if (StringHelper.isNullOrEmpty(p_cent)) {
            return null;
        }
        return StringHelper.amountDivide100(p_cent);
    }

    public String sendBatch(NameValuePair[] data, String url,String p_for) {
        HttpClient httpClient = null;
        PostMethod postMethod = null;
        BufferedReader reader = null;
        InputStream resInputStream = null;
        String result = null;
        try {
            WriteBankLogLn2("提交银行的地址:" + url);
            System.setProperty("https.protocols", "TLSv1");
            httpClient = new HttpClient();
            httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, getCharset("GBK",p_for));
            postMethod = new PostMethod(url);

            // 获得管理参数
            HttpConnectionManagerParams managerParams = httpClient.getHttpConnectionManager().getParams();
            // 设置连接超时时间(单位毫秒)
            managerParams.setConnectionTimeout(40000);
            // 设置读数据超时时间(单位毫秒)
            managerParams.setSoTimeout(120000);
            postMethod.setRequestHeader("Connection", "close");
            postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(1, false));
            postMethod.setRequestBody(data);

            httpClient.executeMethod(postMethod);
            resInputStream = postMethod.getResponseBodyAsStream();

            // 接收返回报文
            reader = new BufferedReader(new InputStreamReader(resInputStream, getCharset("GBK",p_for)));
            String tempBf = null;
            StringBuffer html = new StringBuffer();
            while ((tempBf = reader.readLine()) != null) {
                html.append(tempBf);
            }
            result = html.toString();
            WriteBankLogStep3(result);
        } catch (Exception ex) {
            ex.printStackTrace();

        } finally {

            // 释放httpclient
            if (postMethod != null) {
                postMethod.releaseConnection();

            }
            if (null != httpClient) {
                SimpleHttpConnectionManager manager = (SimpleHttpConnectionManager) httpClient.getHttpConnectionManager();
                if (null == manager) {
                    httpClient.getHttpConnectionManager().closeIdleConnections(0);
                } else {
                    manager.shutdown();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (resInputStream != null) {
                try {
                    resInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return result;
        }
    }

    protected String getCharset2() {
        return getBankConfig().getCharset("GBK", "receipt");
    }

    protected String[] sendToBankReceipt(String p_reqStr, String addr) throws Exception {
        String[] retStr = new String[]{"", ""};
        String charset = getCharset2();//GBK
        WriteBankLogLn("1.提交银行的地址：" + addr);
        String p_string = p_reqStr;
        WriteBankLogStep2(p_string);
        WebRequest tmpWebRequest = new WebRequest(addr);
        tmpWebRequest.setConnectTimeout(60000);
        tmpWebRequest.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        tmpWebRequest.setRequestProperty("connection", "Close");
        String charsetName = charset;
        byte[] tmpToWriteBytes = p_string.getBytes(charsetName);
        retStr = tmpWebRequest.upload(tmpToWriteBytes, charsetName);
        WriteBankLogStep3(retStr[1]);
        return retStr;
    }

}
